/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 check_artifact_influence.cpp

	$Header: /heroes4/check_artifact_influence.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "check_artifact_influence.h"

#include "artifact_effect.h"
#include "artifact_effect_visitor.h"
#include "artifact_prop_ability.h"
#include "artifact_prop_combat.h"
#include "artifact_prop_spell_attack.h"
#include "artifact_prop_spell_cost.h"
#include "battlefield.h"
#include "can_cast.h"
#include "combat_creature.h"
#include "creature_type.h"
#include "external_string.h"
#include "hero.h"
#include "magic_mirror.h"
#include "replace_keywords.h"
#include "spell_properties.h"

extern t_external_string const k_combat_action_cause_effect;

namespace
{
	// ------------------------------------------------------------------
	// visitor which checks artifact influences
	// ------------------------------------------------------------------
	class t_influence_visitor : public t_artifact_effect_visitor
	{
	public:
		t_influence_visitor( t_combat_creature const&    artifact_owner, 
							 t_combat_creature const&	 creature, 
							 int					     distance,
			                 t_creature_influence_bonus& bonus );

		virtual bool visit( t_artifact_effect& effect );
		virtual bool visit_ability( t_artifact_prop::t_give_ability& effect );
		virtual bool visit_combat( t_artifact_prop::t_combat& effect );
		virtual bool visit_spell( t_artifact_prop::t_single_spell& effect );
		virtual bool visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect );
		virtual bool visit_spell_list( t_artifact_prop::t_spell_list_effect& effect );
	protected:
		bool check_target( t_artifact_effect& effect ) const;

		t_combat_creature const&	m_artifact_owner;
		t_creature_influence_bonus& m_bonus;
		t_combat_creature const&	m_creature;
		int							m_distance;
		bool						m_friendly;
	};

	// ------------------------------------------------------------------
	// visitor which sets attributes based on artifacts
	// ------------------------------------------------------------------
	class t_artifact_attribute_visitor : public t_artifact_effect_visitor
	{
	public:
		t_artifact_attribute_visitor( t_combat_creature& creature );

		virtual bool visit( t_artifact_effect& effect );
		virtual bool visit_ability( t_artifact_prop::t_give_ability& effect );
		virtual bool visit_spell( t_artifact_prop::t_single_spell& effect );
		virtual bool visit_spell_list( t_artifact_prop::t_spell_list_effect& effect );
	protected:
		t_combat_creature& m_creature;
	};

};

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
t_influence_visitor::t_influence_visitor( t_combat_creature const& artifact_owner,
										  t_combat_creature const& creature, 
										  int distance,
										  t_creature_influence_bonus& bonus )
				   : m_bonus( bonus ), m_creature( creature ),
				     m_artifact_owner( artifact_owner )
{
	m_distance = distance;
	m_friendly = artifact_owner.is_friendly( creature );
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::check_target( t_artifact_effect& effect ) const
{
	switch (effect.get_target())
	{
		case k_artifact_target_hero: 
			return false;	// already handled by self-setting effects

		case k_artifact_target_enemy_creatures:
			if (m_friendly)
				return false;
			if (m_creature.get_hero() != 0)
				return false;
			break;

		case k_artifact_target_enemy_heroes:
			if (m_friendly)
				return false;
			if (m_creature.get_hero() == 0)
				return false;
			break;

		case k_artifact_target_friendly_creatures:
			if (!m_friendly)
				return false;
			if (m_creature.get_hero() != 0)
				return false;
			break;

		case k_artifact_target_friendly_heroes:
			if (!m_friendly)
				return false;
			if (m_creature.get_hero() == 0)
				return false;
			break;
		
		case k_artifact_target_everything:
			break;
	}
	return true;
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit( t_artifact_effect& effect )
{
	if (!check_target(effect))
		return false;

	switch (effect.get_effect())
	{
		case k_artifact_effect_combat_movement:
			m_bonus.artifact_move_bonus += effect.get_amount();
			break;

		case k_artifact_effect_damage:
			m_bonus.artifact_offense_bonus += effect.get_amount();
			break;

		case k_artifact_effect_luck:
			m_bonus.artifact_luck_bonus += effect.get_amount();
			break;

		case k_artifact_effect_magic_resistance:
			m_bonus.magic_resistance += effect.get_amount();
			break;

		case k_artifact_effect_morale:
			m_bonus.artifact_morale_bonus += effect.get_amount();
			break;

		case k_artifact_effect_speed:
			m_bonus.artifact_speed_bonus += effect.get_amount();
			break;

		case k_artifact_effect_sea_bonus:
		{
			t_battlefield& battlefield = m_creature.get_battlefield();

			if (!battlefield.is_sea_battle())
				return false;
			m_bonus.artifact_offense_bonus += effect.get_amount();
			m_bonus.artifact_defense_bonus += effect.get_amount();
			break;
		}
	}
	return false;
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit_ability( t_artifact_prop::t_give_ability& effect )
{
	if (!check_target( effect ))
		return false;
	m_bonus.abilities.push_back( effect.get_ability() );
	return false;
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit_combat( t_artifact_prop::t_combat& effect )
{
	if (effect.get_effect() != k_artifact_effect_defense)
		return false;
	if (!check_target( effect ))
		return false;
	m_bonus.artifact_defense_bonus += effect.get_amount();
	return false;
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit_spell( t_artifact_prop::t_single_spell& effect )
{
	if (effect.get_effect() != k_artifact_effect_spell)
		return false;
	if (!check_target( effect ))
		return false;
	if (!can_affect( effect.get_spell(), m_creature ))
		return false;
	m_bonus.artifact_spell_effects.push_back( effect.get_spell() );
	return false;
}


// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect )
{
	if (effect.get_effect() != k_artifact_effect_spell_cost)
		return false;
	if (!check_target( effect ))
		return false;
	m_bonus.artifact_spell_cost_modifier += effect.get_percentage();
	return false;
}

// ------------------------------------------------------------------
// visitor which checks artifact influences
// ------------------------------------------------------------------
bool t_influence_visitor::visit_spell_list( t_artifact_prop::t_spell_list_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_immunity)
		return false;
	if (!check_target( effect ))
		return false;

	// add temporary spell immunities to bonus
	t_artifact_prop::t_spell_set const&			 spells = effect.get_spells();
	t_artifact_prop::t_spell_set::const_iterator index;
	t_artifact_spell_immunity					 immunity;

	immunity.artifact_name = m_artifact->get_name( true );
	for (index = spells.begin(); index != spells.end(); ++index)
	{
		immunity.spell = *index;
		m_bonus.artifact_spell_immunities.push_back( immunity );
	}
	return false;
}

// check all influences by artifacts on a creature
void check_artifact_influence( t_combat_creature const& artifact_owner, 
							   t_combat_creature const& creature, 
							   int						distance,
							   t_creature_influence_bonus& bonus )
{
	t_hero const* hero = artifact_owner.get_hero();

	if (hero == 0)
		return;

	t_influence_visitor visitor( artifact_owner, creature, distance, bonus );

	hero->accept( visitor );
}

// ------------------------------------------------------------------
// visitor which sets attributes based on artifacts
// ------------------------------------------------------------------
t_artifact_attribute_visitor::t_artifact_attribute_visitor( t_combat_creature& creature )
                            : m_creature( creature )
{
}

// ------------------------------------------------------------------
// visitor which sets attributes based on artifacts
// ------------------------------------------------------------------
bool t_artifact_attribute_visitor::visit( t_artifact_effect& effect )
{
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;

	switch (effect.get_effect())
	{
		case k_artifact_effect_block:
			m_creature.set_block_chance( effect.get_amount() );
			break;

		case k_artifact_effect_combat_movement:
			m_creature.add_movement( effect.get_amount() );
			break;

		case k_artifact_effect_guardian:
			m_creature.set_guardian_robe( effect.get_amount() );
			break;

		// speed is handled in get_artifact_damage_modifier
	}
	return false;
}

// ------------------------------------------------------------------
// visitor which sets attributes based on artifacts
// ------------------------------------------------------------------
bool t_artifact_attribute_visitor::visit_ability( t_artifact_prop::t_give_ability& effect )
{
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;

	m_creature.set_ability( effect.get_ability() );
	return false;
}

// ------------------------------------------------------------------
// visitor which sets attributes based on artifacts
// ------------------------------------------------------------------
bool t_artifact_attribute_visitor::visit_spell( t_artifact_prop::t_single_spell& effect )
{
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;
	if (effect.get_effect() != k_artifact_effect_spell)
		return false;
	m_creature.set_spell( effect.get_spell(), t_combat_action_message(), 0, k_spell_source_permanent );
	return false;
}

// ------------------------------------------------------------------
// visitor which sets attributes based on artifacts
// ------------------------------------------------------------------
bool t_artifact_attribute_visitor::visit_spell_list( t_artifact_prop::t_spell_list_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_immunity)
		return false;
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;

	// add spell immunities
	t_artifact_prop::t_spell_set const&			 spells = effect.get_spells();
	t_artifact_prop::t_spell_set::const_iterator index;
	t_artifact_spell_immunity					 immunity;

	immunity.artifact_name = m_artifact->get_name( true );
	for (index = spells.begin(); index != spells.end(); ++index)
	{
		immunity.spell = *index;
		m_creature.add_immunity( immunity );
	}
	return false;
}


// --------------------------------------------------------
// check artifacts which set attributes on creature
// --------------------------------------------------------
void check_artifacts( t_combat_creature& creature )
{
	t_hero* hero = creature.get_hero();

	if (hero == 0)
		return;

	t_artifact_attribute_visitor visitor( creature );

	hero->accept( visitor );
}

// --------------------------------------------------------
// check artifacts cast a spell when you attack
// --------------------------------------------------------
namespace
{
	class t_spell_attack_visitor : public t_artifact_effect_visitor
	{
	public:
		t_spell_attack_visitor( t_combat_creature& attacker, t_combat_creature& defender,
							    bool ranged );

		virtual bool visit_spell_attack( t_artifact_prop::t_spell_with_attack_base& effect );
	protected:
		t_combat_creature& m_attacker;
		t_combat_creature& m_defender;
		bool			   m_ranged;
	};
};

// --------------------------------------------------------
// check artifacts cast a spell when you attack
// --------------------------------------------------------
t_spell_attack_visitor::t_spell_attack_visitor( t_combat_creature& attacker, 
											    t_combat_creature& defender,
												bool			   ranged)
					  : m_attacker( attacker ), m_defender( defender )
{
	m_ranged = ranged;
}

// --------------------------------------------------------
// check artifacts cast a spell when you attack
// --------------------------------------------------------
bool t_spell_attack_visitor::visit_spell_attack( t_artifact_prop::t_spell_with_attack_base& effect )
{
	if (!effect.affects( m_ranged ))
		return false;
	
	t_battlefield&  battlefield = m_attacker.get_battlefield();
	std::string		text;

	text = replace_keywords( k_combat_action_cause_effect, "%spell", 
							 get_short_spell_name( effect.get_spell() ) );

	cast_and_mirror( &m_attacker, &m_defender, effect.get_spell(), text );
	return false;
}


void check_artifact_spell_attack( t_combat_creature& attacker,
								  t_combat_creature& defender,
								  bool				 ranged )
{
	t_hero* hero = attacker.get_hero();

	if (hero == 0)
		return;

	t_spell_attack_visitor visitor( attacker, defender, ranged );

	hero->accept( visitor );
}
